﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using EzDesk.Clients.Utility;
using System.Windows.Threading;
using EzDesk.DesktopClient.Storage;
using EzDesk.Generic;
using EzDesk.DesktopClient.Model;
using EzDesk.DesktopClient.Event;
using EzDesk.EventAggregation;
using System.Diagnostics;
using System.Data.SQLite;

namespace EzDesk.DesktopClient
{
    public interface IDataManagement
    {
        DateTime LastLoadDateTime { get; set; }
        void RefreshDataFromServer();
        FeedPushArgs GetDataFromLocal();
        IEnumerable<Feed> GetFeed(int startIndex, int count, FeedType feedType);
    }

    /// <summary>
    /// @TODO:This class should be split into 2 classes.
    /// </summary>
    public class DataManagement : ManagementBase<DataManagement>, IDataManagement
    {
        private const string FEED_CLIENT_TYPE = "xaml";
        private Timer _timer;
        private bool _isRunning;

        private IEnumerable<string> _lastFeedGuidCollection = new List<string>();
        private DateTime _tempLastDate = DateTime.MinValue;
        private readonly DateTime _maxToDatetime = new DateTime(2099, 1, 1);

        public DateTime LastLoadDateTime
        {
            get
            {
                return ClientConfigurationManagement.Instance.LastLoadDatetime;
            }
            set
            {
                ClientConfigurationManagement.Instance.LastLoadDatetime = value;
            }
        }


        public FeedPushArgs GetDataFromLocal()
        {
            var dataUpdateArgs = new FeedPushArgs()
            {
                NotificationCollection = GetFeed(0, ClientConfigurationManagement.Instance.FeedDisplayCount, FeedType.notification),
                UpdateCollection = GetFeed(0, ClientConfigurationManagement.Instance.FeedDisplayCount, FeedType.update),
                ToDoCollection = GetFeed(0, ClientConfigurationManagement.Instance.FeedDisplayCount, FeedType.todo)
            };
            return dataUpdateArgs;
        }

        public void RefreshDataFromServer()
        {
            _lastFeedGuidCollection = new List<string>();

            bool hasNewFeed = this.GetFeedFromServer();
            if (!hasNewFeed) return;

            PublishDataUpdateEvent();
            //时间戳更新
            LastLoadDateTime = _tempLastDate;
            LogEx.TRACE("DataManagement", "时间戳更新为" + this.LastLoadDateTime);
        }

        private void PublishDataUpdateEvent()
        {
            var lastFeedCollection = GetLastUpdateFeedCollection();
            var dataUpdateArgs = new FeedPushArgs()
            {
                NotificationCollection = lastFeedCollection.Where(f => f.Type == FeedType.notification.GetEnumName()).ToList(),
                UpdateCollection = lastFeedCollection.Where(f => f.Type == FeedType.update.GetEnumName()).ToList(),
                ToDoCollection = lastFeedCollection.Where(f => f.Type == FeedType.todo.GetEnumName()).ToList(),
            };
            FeedPushEvent.Instance.Publish(dataUpdateArgs);
        }

        private bool GetFeedFromServer()
        {
            string feedString;
            try
            {
                // 尝试从数据库拿 "上次拿数据时间" 到 "现在" 时间段内的Feed数据
                feedString = CoreClientService.GetAllXmlFeedsBetweenDate(
                   CurrentUser.Id,
                   LastLoadDateTime,
                   _maxToDatetime,
                   null, null, null);
            }
            catch
            {
                return false;
            }

            // 拿到之后把Feed数据转换成XAML
            LogEx.TRACE("ConvertEzmlToXaml", "Ezml: " + feedString);
            FeedCollection feedCollection = FeedCollection.Create(feedString, FEED_CLIENT_TYPE);
            LogEx.TRACE("ConvertEzmlToXaml", "Result, Feed Count: " + feedCollection.Count);

            // 如果一个都没拿到，那么啥都不做
            if (feedCollection.Count == 0) return false;


            // 拿到数据了 把新拿到的Feed XAML丢到数据库里面去
            using (var ezDesktopClientDb = ClientDbStorage.GetDataContext(CurrentUser.UserCode))
            {
                // 新的Feed，插入到数据库里面去
                var feedsToInsert = feedCollection
                    .Where(n => n.CreateOn > LastLoadDateTime)
                    .Select(n => new Feed(n));

                feedsToInsert.Foreach(newFeed =>
                {
                    var existedFeed = ezDesktopClientDb.Feed.FirstOrDefault(f => f.FeedID == newFeed.FeedID);
                    if (existedFeed == null)
                    {
                        ezDesktopClientDb.Feed.InsertOnSubmit(newFeed);
                    }
                });

                // Update到本地数据库，
                var feedsToUpdate = feedCollection
                    .Where(n => n.UpdateOn > LastLoadDateTime || n.LastChangeOn > LastLoadDateTime);

                feedsToUpdate.Foreach(x =>
                        {
                            var feedToUpdate = ezDesktopClientDb.Feed.FirstOrDefault(y => y.FeedID == x.FeedId.ToString());
                            if (feedToUpdate != null)
                            {
                                feedToUpdate.CopyFrom(x);
                            }
                        });

                ezDesktopClientDb.SubmitChanges();
            }

            //通过遍历最新拿回来的feed的最后更新和最后变化两个字段来获取最后获取的时间
            var maxDate = feedCollection.Max(f => f.LastChangeOn > f.UpdateOn ? f.LastChangeOn : f.UpdateOn);
            _tempLastDate = _tempLastDate > maxDate ? _tempLastDate : maxDate;

            //获取本次更新的数据集
            _lastFeedGuidCollection = _lastFeedGuidCollection.Union(feedCollection.Select(f => f.FeedId.ToString()));
            return true;
        }

        public void Start()
        {
            if (_isRunning) return;

            _isRunning = true;

            //todo: 控制从服务端拿数据的时间
            _timer = new Timer(obj => RefreshDataFromServer(), null, 6666, 10000);
        }

        public void Stop()
        {
            if (_isRunning)
            {
                _timer.Dispose();
                _timer = null;
            }

            _isRunning = false;
        }

        public DataManagement()
            : base()
        {
            InitEvent();
        }

        private void InitEvent()
        {
            LoginEvent.Instance.Subscribe(this.OnUserLogin, ThreadOption.PublisherThread);
            LoginedEvent.Instance.Subscribe(this.AfterUserLogin, ThreadOption.PublisherThread);
            LogoutEvent.Instance.Subscribe(this.OnUserLogout, ThreadOption.BackgroundThread);
        }

        private void OnUserLogin(LoginArgs args)
        {
            Start();
        }

        private void AfterUserLogin(LoginArgs args)
        {

        }

        private void OnUserLogout(LogoutArgs args)
        {
            Stop();
        }


        // 获取_lastFeedGuidCollection中标明的这次更新的所有Feed
        private IEnumerable<Feed> GetLastUpdateFeedCollection()
        {
            LogEx.TRACE("GetFeedByGuid", DateTime.Now.ToShortDateString());
            using (var ezDesktopClientDb = ClientDbStorage.GetDataContext(CurrentUser.UserCode))
            {
                return _lastFeedGuidCollection
                    .Select(guid => ezDesktopClientDb.Feed.FirstOrDefault(f => f.FeedID == guid))
                    .Where(feedItem => feedItem != null)
                    .ToList();
            }
        }


        public IEnumerable<Feed> GetFeed(int startIndex, int count, FeedType feedType)
        {
            LogEx.TRACE("GetFeed", DateTime.Now.ToShortDateString());
            string type = feedType.GetEnumName();

            EzDesktopClientDb dataContext = ClientDbStorage.GetDataContext(CurrentUser.UserCode);

            return dataContext.Feed
                            .Where(feed => feed.Type == type && feed.IsAvailable == true)
                            .OrderByDescending(feed => feed.UpdateOn)
                            .Take(count)
                            .ToList();
        }
    }
}
